package valuecomusa.fijibus.eticket.ws;

import android.content.Context;
import android.util.Log;
import com.google.gson.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import valuecomusa.fijibus.eticket.Utilities;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;


public class RESTClient
{
    private final String TAG = "RESTClient";
    private Context Context;

    public RESTClient(Context cxt)
    {
        this.Context = cxt;
    }

    private String convertStreamToString(InputStream is)
    {
        /*
         * To convert the InputStream to String we use the BufferedReader.readLine()
         * method. We iterate until the BufferedReader return null which means
         * there's no more data to read. Each line will appended to a StringBuilder
         * and returned as String.
         */
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try
        {
            while ((line = reader.readLine()) != null)
            {
                sb.append(line + "\n");
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                is.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

    public RestResultValues<CountInfo> doGetDataCount(String url)
    {
        if(url == null || url.length() == 0)
            return null;

        Log.d(TAG, "[doGetDataCount] " + url);

        HttpClient httpClient = new DefaultHttpClient();

        //String url = Context.getString(R.string.sunbeam_ws_url) + "/api/bus?count";

        // Prepare a request object
        HttpGet httpGet = new HttpGet(url);

        // Execute the request
        HttpResponse response;
        try
        {
            response = httpClient.execute(httpGet);
            // Examine the response status
            Log.i(TAG, "[doGetDataCount] Status: " + response.getStatusLine().toString());

            if (response.getStatusLine().getStatusCode() != 200) //OK
                return null;

            // Get hold of the response entity
            HttpEntity entity = response.getEntity();
            // If the response does not enclose an entity, there is no need
            // to worry about connection release

            if (entity != null)
            {
                // A Simple JSON Response Read
                InputStream entityContent = entity.getContent();
                String result = convertStreamToString(entityContent);
                // Closing the input stream will trigger connection release
                entityContent.close();
                Log.i(TAG, "[doGetDataCount] Result: " + result);

                JsonParser jp = new JsonParser();
                JsonElement element = jp.parse(result);

                RestResultValues<CountInfo> rv = new RestResultValues<CountInfo>();

                rv.Message = element.getAsJsonObject().get("Message").getAsString();
                rv.Code = element.getAsJsonObject().get("Code").getAsInt();
                if (rv.Code == 0 //success
					&& !(element.getAsJsonObject().get("Data") instanceof com.google.gson.JsonNull))
                {
                    JsonArray data = element.getAsJsonObject().get("Data").getAsJsonArray();
                    rv.Data = new ArrayList<CountInfo>();

                    CountInfo mc = new CountInfo();
                    mc.Count = data.get(0).getAsJsonObject().get("Count").getAsInt();
                    mc.From = data.get(0).getAsJsonObject().get("From").getAsInt();

                    rv.Data.add(mc);
                }

                return rv;
            }
        }
        catch (ClientProtocolException e)
        {
            Log.e(TAG, "[doGetDataCount] ClientProtocolException:: " + e.toString());
        }
        catch (IOException e)
        {
            Log.e(TAG, "[doGetDataCount] IOException:: " + e.toString());
        }
        return null;
    }

    public <T> RestResultValues<T> doGetData(String url, Class<T> cls)
    {
        Log.d(TAG, "[doGetData] " + url);
        HttpClient httpClient = new DefaultHttpClient();

        // Prepare a request object
        HttpGet httpGet = new HttpGet(url);

        // Execute the request
        HttpResponse response;
        try
        {
            response = httpClient.execute(httpGet);
            // Examine the response status
            Log.i(TAG, "[doGetData] Status: " + response.getStatusLine().toString());

            if (response.getStatusLine().getStatusCode() != 200) //OK
                return null;

            // Get hold of the response entity
            HttpEntity entity = response.getEntity();
            // If the response does not enclose an entity, there is no need
            // to worry about connection release

            if (entity != null)
            {
                // A Simple JSON Response Read
                InputStream entityContent = entity.getContent();
                String result = convertStreamToString(entityContent);
                // Closing the input stream will trigger connection release
                entityContent.close();
                Log.i(TAG, "[doGetData] Result: " + result);

                JsonParser jp = new JsonParser();
                JsonElement element = jp.parse(result);
                //---
                RestResultValues<T> rv = new RestResultValues<T>();
                //---
                rv.Message = element.getAsJsonObject().get("Message").getAsString();
                rv.Code = element.getAsJsonObject().get("Code").getAsInt();
                //---
                if (rv.Code == 0) //success
                {
                    if(!(element.getAsJsonObject().get("Data") instanceof com.google.gson.JsonNull))
                    {
                        JsonArray data = element.getAsJsonObject().get("Data").getAsJsonArray();
                        rv.Data = new ArrayList<T>();

                        for(JsonElement el: data)
                        {
                            T mem = cls.newInstance();

                            for(Field field: cls.getFields())
                            {
                                if(el.getAsJsonObject().has(field.getName()))
                                {
                                    Type type = field.getGenericType();
                                    if(type instanceof ParameterizedType)
                                        continue; //not process yet
                                    //--
                                    type = field.getType();
                                    if(type.equals(String.class))
                                        field.set(mem, el.getAsJsonObject().get(field.getName()).getAsString());
                                    else if(type.equals(Long.TYPE))
                                        field.set(mem, el.getAsJsonObject().get(field.getName()).getAsLong());
                                    else if(type.equals(Date.class))
                                        field.set(mem, Utilities.StringToDate(el.getAsJsonObject().get(field.getName()).getAsString()));
                                    else if(type.equals(Integer.TYPE))
                                        field.set(mem, el.getAsJsonObject().get(field.getName()).getAsInt());
                                    else if(type.equals(Double.TYPE))
                                        field.set(mem, el.getAsJsonObject().get(field.getName()).getAsDouble());
                                }
                            }

                            rv.Data.add(mem);
                        }
                    }
                }

                return rv;
            }
        }
        catch (ClientProtocolException e)
        {
            Log.e(TAG, "[doGetData] ClientProtocolException:: " + e.toString());
			e.printStackTrace();
        }
        catch (IOException e)
        {
            Log.e(TAG, "[doGetData] IOException:: " + e.toString());
			e.printStackTrace();
        }
        catch (Exception e)
        {
            Log.e(TAG, "[doGetData] Exception:: " + e.toString());
			e.printStackTrace();
        }
        return null;
    }

    public <T> RestResultValuesExt<T> doGetDataExt(String url, Class<T> cls)
    {
        Log.d(TAG, "[doGetDataExt] " + url);
        HttpClient httpClient = new DefaultHttpClient();

        // Prepare a request object
        HttpGet httpGet = new HttpGet(url);

        // Execute the request
        HttpResponse response;
        try
        {
            response = httpClient.execute(httpGet);
            // Examine the response status
            Log.i(TAG, "[doGetDataExt] Status: " + response.getStatusLine().toString());

            if (response.getStatusLine().getStatusCode() != 200) //OK
                return null;

            // Get hold of the response entity
            HttpEntity entity = response.getEntity();
            // If the response does not enclose an entity, there is no need
            // to worry about connection release

            if (entity != null)
            {
                // A Simple JSON Response Read
                InputStream entityContent = entity.getContent();
                String result = convertStreamToString(entityContent);
                // Closing the input stream will trigger connection release
                entityContent.close();
                Log.i(TAG, "[doGetDataExt] Result: " + result);

                JsonParser jp = new JsonParser();
                JsonElement element = jp.parse(result);
                //---
                RestResultValuesExt<T> rv = new RestResultValuesExt<T>();
                //---
                rv.Message = element.getAsJsonObject().get("Message").getAsString();
                rv.Code = element.getAsJsonObject().get("Code").getAsInt();
                //---
                if (rv.Code == 0) //success
                {
                    rv.Count = element.getAsJsonObject().get("Count").getAsInt();
                    rv.From = element.getAsJsonObject().get("From").getAsInt();
                    rv.To = element.getAsJsonObject().get("To").getAsInt();
                    //---
                    if(!(element.getAsJsonObject().get("Data") instanceof com.google.gson.JsonNull)
						&& element.getAsJsonObject().get("Data") != null)
                    {
                        JsonArray data = element.getAsJsonObject().get("Data").getAsJsonArray();
                        rv.Data = new ArrayList<T>();

                        for(JsonElement el: data)
                        {
                            T mem = cls.newInstance();

                            for(Field field: cls.getFields())
                            {
                                if(el.getAsJsonObject().has(field.getName()))
                                {
                                    Type type = field.getGenericType();
                                    if(type instanceof ParameterizedType)
                                        continue; //not process yet
                                    //--
                                    type = field.getType();
                                    if(type.equals(String.class))
                                        field.set(mem, el.getAsJsonObject().get(field.getName()).getAsString());
                                    else if(type.equals(Long.TYPE))
                                        field.set(mem, el.getAsJsonObject().get(field.getName()).getAsLong());
                                    else if(type.equals(Date.class))
                                        field.set(mem, Utilities.StringToDate(el.getAsJsonObject().get(field.getName()).getAsString()));
                                    else if(type.equals(Integer.TYPE))
                                        field.set(mem, el.getAsJsonObject().get(field.getName()).getAsInt());
                                    else if(type.equals(Double.TYPE))
                                        field.set(mem, el.getAsJsonObject().get(field.getName()).getAsDouble());
                                }
                            }

                            rv.Data.add(mem);
                        }
                    }
                }

                return rv;
            }
        }
        catch (ClientProtocolException e)
        {
            Log.e(TAG, "[doGetDataExt] ClientProtocolException:: " + e.toString());
			e.printStackTrace();
        }
        catch (IOException e)
        {
            Log.e(TAG, "[doGetDataExt] IOException:: " + e.toString());
			e.printStackTrace();
        }
        catch (Exception e)
        {
            Log.e(TAG, "[doGetDataExt] Exception:: " + e.toString());
			e.printStackTrace();
        }
        return null;
    }

    public RestResultValues<String> doGetStringData(String url)
    {
        Log.d(TAG, "[doGetStringData] " + url);
        HttpClient httpClient = new DefaultHttpClient();

        // Prepare a request object
        HttpGet httpGet = new HttpGet(url);

        // Execute the request
        HttpResponse response;
        try
        {
            response = httpClient.execute(httpGet);
            // Examine the response status
            Log.i(TAG, "[doGetStringData] Status: " + response.getStatusLine().toString());

            if (response.getStatusLine().getStatusCode() != 200) //OK
                return null;

            // Get hold of the response entity
            HttpEntity entity = response.getEntity();
            // If the response does not enclose an entity, there is no need
            // to worry about connection release

            if (entity != null)
            {
                // A Simple JSON Response Read
                InputStream entityContent = entity.getContent();
                String result = convertStreamToString(entityContent);
                // Closing the input stream will trigger connection release
                entityContent.close();
                Log.i(TAG, "[doGetStringData] Result: " + result);

                JsonParser jp = new JsonParser();
                JsonElement element = jp.parse(result);
                //---
                RestResultValues<String> rv = new RestResultValues<String>();
                //---
                rv.Message = element.getAsJsonObject().get("Message").getAsString();
                rv.Code = element.getAsJsonObject().get("Code").getAsInt();
                //---
                if (rv.Code >= 0 //success
                        && !(element.getAsJsonObject().get("Data") instanceof com.google.gson.JsonNull))
                {
                    //---
                    JsonArray data = element.getAsJsonObject().get("Data").getAsJsonArray();
                    rv.Data = new ArrayList<String>();

                    for(JsonElement el: data)
                    {
                        rv.Data.add(el.getAsString());
                    }
                }

                return rv;
            }
        }
        catch (ClientProtocolException e)
        {
            Log.e(TAG, "[doGetStringData] ClientProtocolException:: " + e.toString());
			e.printStackTrace();
        }
        catch (IOException e)
        {
            Log.e(TAG, "[doGetStringData] IOException:: " + e.toString());
			e.printStackTrace();
        }
        catch (Exception e)
        {
            Log.e(TAG, "[doGetStringData] Exception:: " + e.toString());
			e.printStackTrace();
        }
        return null;
    }

    public RestResultValues<String> doPostData(String url, String xContent)
    {
        try
        {
			Log.i(TAG, "[doPostData] " + url);
			HttpClient httpClient = new DefaultHttpClient();
			// Prepare a request object
			HttpPost httpPost = new HttpPost(url);
			//--
            StringEntity input = new StringEntity(xContent);
            input.setContentType("application/json");
            httpPost.setEntity(input);
			//--
            HttpResponse response = httpClient.execute(httpPost);
            Log.i(TAG, "[doPostData] Status: " + response.getStatusLine().toString());

            if (response.getStatusLine().getStatusCode() != 200) //OK
                return null;

            // Get hold of the response entity
            HttpEntity entity = response.getEntity();
            // If the response does not enclose an entity, there is no need
            // to worry about connection release

            if (entity != null)
            {
                // A Simple JSON Response Read
                InputStream entityContent = entity.getContent();
                String result = convertStreamToString(entityContent);
                // Closing the input stream will trigger connection release
                entityContent.close();
                Log.i(TAG, "[doPostData] Result: " + result);

                JsonParser jp = new JsonParser();
                JsonElement element = jp.parse(result);
                //---
                RestResultValues<String> rv = new RestResultValues<String>();
                //---
                rv.Message = element.getAsJsonObject().get("Message").getAsString();
                rv.Code = element.getAsJsonObject().get("Code").getAsInt();
                //---
                if (rv.Code == 0
						&& !(element.getAsJsonObject().get("Data") instanceof com.google.gson.JsonNull)) //success
                {
                    //---
                    JsonArray data = element.getAsJsonObject().get("Data").getAsJsonArray();
                    rv.Data = new ArrayList<String>();

                    for(JsonElement el: data)
                    {
                        rv.Data.add(el.getAsString());
                    }
                }

                return rv;
            }
        }
        catch (Exception ex)
        {
            Log.e(TAG, "[doPostData] Exception:: " + ex.toString());
			ex.printStackTrace();
        }

        return null;
    }
}
